#ifndef __FAT_H_
#define __FAT_H_

#include <type.h>
#include <ds/mutex.h>
#include <fs.h>

#define FAT32_CLUSTER_FREE      0
#define FAT32_CLUSTER_RESERVED  1
#define FAT32_CLUSTER_BROKEN    0x0FFFFFF7
#define FAT32_CLUSTER_END       0x0FFFFFF8 // any bigger than this represents a final cluster

typedef struct fat32_extended_boot_record {
    u32 fat_size; // Sectors per FAT
    u16 extended_flags;
    u16 fat_version;
    u32 root_cluster;
    u16 fat_info;
    u16 backup_BS_sector;
    u8 reserved_0[12];
    u8 drive_number;
    u8 reserved_1;
    u8 signature;
    u32 volume_id;
    u8 volume_label[11];
    u8 fat_type_label[8];
} packed fat32_extBS_t;

typedef struct fat32_bios_parameter_block {
    u8 EB3C90[3];
    u8 OEM_id[8];
    u16 sector_size; // bytes per sector
    u8 cluster_size; // sector per cluster
    u16 number_reserved_sector;
    u8 fat_count; // number of fats
    u16 number_root_entry; // number of root entries
    u16 number_sector;
} packed fat32_BPB_t;

typedef struct fat32_boot_fsinfo {
    u32 signature1;	/* 0x41615252L */
    u32 reserved1[120];	/* Nothing as far as I can tell */
    u32 signature2;	/* 0x61417272L */
    u32 free_clusters;	/* Free cluster count.  -1 if unknown */
    u32 next_cluster;	/* Most recently allocated cluster */
    u32 reserved2[4];
} fat32_fsinfo_t;

#define READ_ONLY   0x01
#define HIDDEN      0x02
#define SYSTEM      0x04
#define VOLUME_ID   0x08
#define DIRECTORY   0x10
#define ARCHIVE     0x20
#define LFN         0x0F

typedef struct short_directory_entry {
    char name[11];
    u8 flag;
    u8 reserved;
    u8 create_time_ms;
    u16 create_time;
    u16 create_date;
    u16 last_access_date;
    u16 cluster_h;
    u16 last_modify_time;
    u16 last_modify_date;
    u16 cluster_l;
    u32 file_length;
} packed sdire_t;

typedef struct long_directory_entry {
    u8 order;
    u8 unicode_name1[10];
    u8 flag;
    u8 reserved;
    u8 checksum;
    u8 unicode_name2[12];
    u16 cluster_number; // normally set it 0
    u8 unicode_name3[4];
} packed ldire_t;

typedef struct {
    mutex_t lock;
    u8 fats;
    u32 fat_start;
    u32 fat_length; // Sectors per FAT
    u32 data_start;
    u32 cluster_size;
    u32 root_cluster;
    u32 max_cluster;
    int prev_free;
    int free_clusters; /* -1 if undefined */
} fat_sb_t;

typedef struct {
    int start;		/* first cluster or 0 */
    inode_t* vfs_inode;
} fat_inode_t;

void fat32_init();
int fat_next_clus(inode_t* inode, u32 cluster, u32* next_cluster);
int fat_get_cluster(inode_t* inode, u32 cluster, void* buf);
int fat_nextN_clus(inode_t* inode, u32 cluster, u32 offset_clus, u32* next_cluster);
int fat_get_dtype(u8 flag);

void fat_fill_inode(inode_t* inode, sdire_t* sdire);

int fat32_file_write(inode_t* inode, void* buf, u32 len, u32 fpos);
int fat32_file_read(inode_t* inode, void* buf, u32 len, u32 fpos);
int fat32_file_close(file_t* file);
int fat32_file_sync(file_t* file);
int fat32_file_seek(inode_t* inode, size_t offset);
int fat32_inode_open(inode_t* this, file_t* file);
int fat32_inode_rename(inode_t* from_inode, dentry_t* from_dentry, dentry_t* to_dentry);
int fat32_inode_sync(inode_t* this);
void fat32_init_inode(sb_t* sb, inode_t* inode);
int fat32_inode_lookup(inode_t* this, dentry_t* dentry);
int fat32_inode_rmdir(inode_t* this, dentry_t* dir);
int fat32_inode_mkdir(inode_t* this, dentry_t* dir);
int fat32_file_readdir(file_t* file, dirent_t* dirent);

#endif // __FAT_H_